A cook's tour of the RP/M O/S:

Note: this document is not intended to replace the full RP operation
manual.

The fundamental concept of rp is it's management of "remote partitions"
to run application programs.  To understand this, we'll take a look at
the standard CP/M run concept.  All operating systems owe to the concept
of a program that is continually resident in the system, providing both
an interactive program to allow user control of the running of
application programs, and a set of "system services" designed to
simplify program construction.  In CP/M, the system occupies the top
portion of memory, and the application program the lower half.  This
scheme has the characteristics:

    1.  The O/S and the applications program are mutually exclusive;
        the space occupied by the system is unavailable to the applications
        program, and vice - versa.

    2.  The applications program is not prevented from direct access to
        O/S code, and therefore may "crash" the system by corrupting
        the O/S code.

    3.  The system relies on "good conduct" of the applications program
        in calling system services.

In any system, the reality is that the O/S and application must make use
of the same memory, and the same processor.  However, two basic hardware
aids can advance the level of the system considerably.  These are a good
system memory management scheme and the use of hardware timers with
interrupts.

Memory management allows the O/S and/or the application to see their own
"virtual space", which allows the program to forget about the other
programs running in the same processor, and makes "relocation"
unnecessary.  The programs can be protected from each other either
implicitly, such as a trap generated by the memory management system if
a program tries to access.

Protection can also be inherent; if the memory management defines all
available program memory without including the other program, the
application simply does not have a means to damage the O/S.

Finally, a hardware timer allows the system to set a maximum "time
limit" for the execution of a program; if the program is crashed or
simply uncooperative, it cannot permanently hold the CPU.

A side benefit of the memory management for an address limited CPU such
as the Z80 is that the program may have a full (64kb) partition or
"virtual address space" to itself.   Both the operating system and the
application program can make use of the full address space available.

The Z280 provides advanced abilities to set up partitions.  Also present
are tools for the system to transfer bytes in and out of the partition
(but not allow program access to the O/S!), and for an organized exit
back to the system from a partition.

Besides the various traps and error conditions, the program obtains
system services by the "SC" (system call) instruction.  65536 possible
system calls are possible, but in reality a system call is one call, to
one location, in the O/S.  This simplifies the work of keeping track of
the application considerably.

The existence of a system call allows the program to make full use of
it's memory space, without the problems associated with CP/M.  It may
start at address 0 (vs.  $100), and use all the memory space it requires
up until the end of memory at $ffff.

This is indeed how the "native" service system for rp works.  This will
be discussed at the end of this tour.

O/S emulation:

With enough effort, there probably isn't any previous Z80 O/S
environment that can't be emulated.  The Z280 can simulate any memory
arrangement, even trap and emulate I/O instructions.  How much trouble
it is, however, is another story.  Fortunately, CP/M was constructed
according to a few basic rules which make things simpler (and in my
option had a lot to do with CP/M's portability):

    1.  The system calls all occur through a single "vector"
        (the BDOS jump).

    2.  No direct access to system data is allowed.

    3.  No specific location of the O/S was ever set.

Of course, that's the way CP/M started out!  Since then, two basic
violations of these rules have become standard practice.  Programs
directly access the set of BIOS vectors, and directly setting the I/O
connection byte (iobyte) before making a system call.  This is done even
by the original CP/M utility "pip".  By and large, however, CP/M is a
very reasonable target for emulation.  The common example of running
under coprocessors on an Apple or IBM - PC is proof of that.

All right, so how do we emulate CP/M under rp/m? looking at the basic
layout of a CP/M partition:

                     -----------------
                     | System page   | --> WBOOT vector
                     |               | --> BDOS vector
                     -----------------
                     | TPA           |
                     |               |
                     |               |

                     |               |
                     |               |
                     -----------------
                     | System        |
                     |               |
                     -----------------

Several assumptions are standard.  The BDOS vector both points to the
entrance to the system services package, and also defines the last
usable TPA byte plus one.  The WBOOT vector points to the entrance to
the warm boot routine, and incidentally indexes the BIOS vector table +
3.  In the system page are defined several standard pieces of data,
including current drive, user, I/O connection, and the command line and
parameters passed to the program.

The layout for RP looks like this:

         ---------------                   ------------------
         | RP          |                   | System page    |
         |             |                   ------------------
         |             |                   | TPA            |
         |             |                   |                |
         |             |                   |                |

         |             |                   |                |
         |             |                   |                |
         |             |                   ------------------
         |             | <<------------->> | System         |
         |             |                   | Communications |
         |             |                   | Area (SCA)     |
         ---------------                   ------------------

First of all, the applications program runs in an entirely different
partition from the system.  It's only method of getting back to the
system partition is by a system call, or by tripping some error
condition, such as execution of an illegal instruction, access or other
violation.

The system page is simply an emulation of the normal CP/M system page.

The current drive, user, iobyte and command parameters are all updated
by the O/S.  The WBOOT and BDOS jumps index areas in the SCA for
emulation.

The SCA totally replaces the BDOS and BIOS sections in a CP/M system.

Basically, all BDOS and BIOS function calls are turned into system calls
here.  There is, then, only the one entry back to the system: the SC
instruction.  When rp receives a system call, it emulates the CP/M
action of the associated function.

MULTIPLE TASK:

Going from this to a multiple task situation only requires that instead
of just one program partition, we implement N many partitions, each with
their own CP/M compatible environment.  Rp must "traffic" the system
requests to and from each partition, simultaineously with other
partition calls.

Internally to rp, there are only "tasks", the number of which is only
limited by the system memory available:

        --------------------
        | rp               |
        |                  |
        |  --------------  |
        |  | Task       |  |
        |  | Control    |  |
        |  | Block      |  |
        |  --------------  |
        |                  |
        |  --------------  |         -----------------
        |  | TCB        |----------->| Partition     |
        |  --------------  |         -----------------
        |                  |
        |  --------------  |         -----------------
        |  | TCB        |----------->| Partition     |
        |  --------------  |         -----------------
        |                  |
        --------------------

Tasks executed in parallel by rp may execute entirely within the O/S
partition, or they may be allocated an applications partition, and
therefore run a CP/M application.

THE RP MONITOR:

The users view of the system usually is via the rp built - in monitor.

All the commands found in CP/M, plus a few more, are implemented.

The basic command line for rp appears as:

     <command> <parameters> {[;/&] <command> <parameters>}

Much as a standard CP/M command line, except that any number of commands
may be entered on a line, separated by either ";" or "&".  Separation of
commands by ";" causes each command to be executed, in turn, with rp
waiting for the last one to complete before starting the next.

Separation of commands by "&" means that rp will not wait for the last
command to complete, but go on to start the next one (in parallel).

commands are one of the following:

    1.  Aliases.  If the command given matches an alias name,
        that is executed.

    2.  Built - in.  If the command given matches an internal
        command, that is executed

    3.  Submit.  If a file by the name command.sub exists, that
        is interpreted under the rules for an exec file.

    4.  Application program.  If a file by the name of command.com
        or command.pgm exists, that is given it's own partition
        and executed.

Note that these command types are executed in the order given.  If an
alias is defined with the same name as a application program, the alias
is executed first.  The search mechanism can be circumvented by directly
specifying what type of command is meant.  Therefore command.com can
only be a CP/M command, command.sub can only be a submit, d0:command can
only be an application or submit, etc.

ALIASES AND SUBMITS:

An alias is defined by the alias command:

     alias command <line of text for the alias>

All the text following the alias name is entered into the alias
definition.

When the alias is invoked, the contents of the alias are executed.

Note that ALL the remaining line past the alias command is entered into
the alias definition, including any ";" or "&" characters (therefore
nothing further is executed after the alias definition).  The current
aliases defined may be listed out via the lalias command.

In the case of a submit, the submit file is executed.

Both the contents of an alias and a submit are the same command lines as
can be executed directly.

When a submit or alias is executed, each line will be printed out before
it is executed, along with the name of the alias/submit, and the number
of the line being executed.

If printout is not desired, the commands noshowsubmit, or noshowalias
will turn this off.  The commands showsubmit, or showalias will turn the
print back on again.

Aliases and submits can be executed within other aliases or submits,
with out any limit on the nesting level.

STANDARD ALIASES:

You have probably noticed that the built - commands are somewhat
verbose.

This is intentional.  The long form command names are better for
insertion in a alias or submit, to make them self - documenting, and
they are less likely to conflict with the names of applications
programs.  Finally, new names for built - in commands can be created to
your heart's content using the alias mechanism.  For the rest of this
tour, we will place the aliases we use for commands after the proper
name as command (alias).

Some of the commands already discussed are: showsubmit (shows),
noshowsubmit (noshows), showalias (showa), noshowalias (noshowa).

FILE SPECIFICATION:

Files under rp consist of a file name, with optional extension.  Where
allowed, The characters "*" and "?" can be used to match multiple
filenames.  Otherwise, the characters "a" - "z", the digits, and the
character "_".  Note that rp does not allow ANY character to appear in a
filename as was the case in CP/M.

This is enforced in exchange for the advanced command processor.  For
the disk area prefix, all of the following are valid:

     d:    - Drive.

     d0:   - Drive/user.

     name: - Directory name.

PATHS:

Three paths may be set by command.  commandpath (cpath) sets the search
path for commands, submitpath (spath) sets the search path for submits,
and helppath (hpath) sets the path for help files.  The form is:

     cpath a0: b: command:

Any number of valid area names may be specified.

MACRO SUBSITUTION:

Macros are character sequences that are expanded in each line before
execution.  They are introduced by the "!" character.  The macros
available are:

     !! - The character "!".

     !<digit> - A word from the calling line, from the 1st to the ninth.

     !<name>  - A string variable.

     !(<expression>) - A string expression.

     !$ - The current time/date.

     !# - The current drive, in lower case.

     !% - The current drive, in upper case.

     !^ - The current drive/user, in lower case.

     !& - The current drive/user, in upper case.

     !* - The current directory label.

     !~ - The entire calling command line.

The typical use of macros is to get parameters from the calling line for
an alias or submit.  The usual "0" - "9" is available, but the entire
line can be processed to recover parameters past the ninth.

EXPRESSIONS:

A full expression processor is built in to rp.  The key to understanding
rp expressions is to realize that the only "type" operated on is the
string.

When a number is given, it is checked for proper numeric format, but
actually entered as a string.  When a math operator is given, the
strings given as operands are converted to a value, the operation
performed, then the result converted back to a string.  The point of
this is to dispense with the need for string conversion operators.  This
gives the expression processor the type freedom as found in the "snobol"
language.  The basic objects that can be operated on are:

    1.  'constant' - Any quoted sequence of characters is a string.

        the " character is the same as any other character.  Two
        quotes back - to - back are interpreted as a single quote.

        The character "\" is used to "force" the next character,
        including a quote or "\".  Additionally, a standard ascii
        control mnemonic such "\eot", or "\cr" may appear, or
        if followed by a number, the ascii equivalent will be
        entered.

    2.  <number> - Numbers, in the range 0 - 65535 may be entered.

        These may be preceded by "$" (hex), "@" (octal), "%" (binary).

    3.  <variable> - A character sequence beginning with a letter or "_",
        and followed by any sequence of letters, digits or "_" is a
        string variable.

Note that macros can be used in expressions, as: '!1' would be a string
containing the first caller parameter.

The operators, in precedence order are:

     <a> = <b>     - Numeric equality.

     <a> == <b>    - String equality.

     <a> # <b>     - Numeric inequality.

     <a> ## <b>    - String inequality.

     <a> < <b>     - Numeric less than.

     <a> << <b>    - String less than.

     <a> > <b>     - Numeric greater than.

     <a> >> <b>    - String greater than.

     <a> <= <b>    - Numeric less than or equal.

     <a> <<= <b>   - String less than or equal.

     <a> => <b>    - Numeric greater than or equal.

     <a> =>> <b>   - String greater than or equal.

     <a> + <b>     - Addition.

     <a> ++ <b>    - Concatenation.

     <a> - <b>     - Subtraction
     <a> or <b>    - Logical "or".

     <a> * <b>     - multiplication.

     <a> ** <b>    - String a replicated b times.

     <a> / <b>     - Division.

     <a> mod <b>   - Modulo.

     <a> xor <b>   - Logical "xor".

     <a> shl <b>   - Value a shifted left b times.

     <a> shr <b>   - Value a shifted right b times.

     <a>[<b>]      - The bth character of string a.

     <a>[<b>,<c>]  - The characters in string a from position b
                     to position c.

     <a>[~<b>]     - The bth word of string a.

     <a>[~<b>,<c>] - The words in string a from position b
                     to position c.

     +<a>          - The positive value of a.

     -<a>          - The negative value of a.

     not <a>       - The logical "not" of a.

     $$<a>         - The hexadecimal conversion of a.

     @@<a>         - The octal conversion of a.

     %%<a>         - The binary conversion of a.

     ^<a>          - The number of characters in string a.

     ~<a>          - The number of words in string a.

     ?<a>          - A string containing the files matching the file
                     specification a.

Typical expressions:

     2+2                     - The single character string '3'.

     2++2                    - The string '22'.

     2+'4'                   - The string '6'.

     2++'4'                  - The string '24'.

     ~'the rain in spain'    - The string '4'.

     'hi'**5                 - The string 'hihihihihi'.

     'special'[5]            - The string 'i'.

     'the rain in spain'[~3] - The string 'in'.

     $$10                    - The string '$a'.

     5 < 10                  - The string '65535'.

     10 < 5                  - The string '0'.

     ?'test.*'               - A string containing all files 'test' of any
                               extension.

COMMON COMMANDS:

The directory (dir) (ls) (files) (cat) command will list the directory
of the current disk area, or any given area(s).  The type command types
out the contents of the given files(s).  The directories (pwd) (map)
command will print all disk areas (that are named) available for use.
The erase (era) (scratch) command will erase the given files.  copy will
copy file(s) from anywhere to anywhere, and will also concatenate a
group of files into one, or move multiple files, or even move files and
change the names of the files at the same time.  move is similar to
copy, except the source will be deleted if the move is successful.  The
help command without parameters gives a list of helps available, and
given a subject, will type out the help file for that.

Echo types a line of text, and calculate (calc) types the result of a
string expression.  The comment (c) command causes the rest of the line
after the command to be ignored.  The prompt command sets the current
command prompt.

Assign assigns the value of a string variable, and input inputs a string
from the console to a string variable.  time prints or sets the current
system time.  Clock prints the system CPU clock speed, and tick prints
the multiprocessor tick time.

Mount and unmount cause a disk to be either placed on line, or taken off
line.

Rp is a strictly mount oriented system; all disks, even floppys, must be
specifically mounted and unmounted to prevent malfunction.  Although
this may seem a rather painful restriction, it speeds O/S operations,
because the disk does not have to be continually "checksummed" to see if
it has been changed, and disk sectors may be buffered to and from the
disk.

The connect statement allows the connection of logical to physical
devices, as:

     connect lst:=lpt: - Connect logical list to lpt:
     connect con:=crt: - Connect logical console to crt:

For more information on these commands, use the help function under rp.

RUNNING APPLICATIONS PROGRAMS:

Besides just running an application program, several controls are
available.

Normally, CP/M translates the command line given the program to upper
case.

This causes many programs problems, such as find, which can't be used in
a straight - forward way to look for lower case.  nouppercase will allow
lower case command lines to be passed to the program, and uppercase
reverses this.  Whether or not an application can perform direct I/O
(execute an "in" or "out" instruction is controlled by the directio and
nodirectio commands.

If it is not desired to allow the program to access the disk directly
via BIOS sector calls, the commands readdirect, noreaddirect,
writedirect, and nowritedirect will allow/disallow this.  Whether or not
the program can set the attributes of a file (and thus, say, set a read
only file back to read/ write) is controlled via the setattribute and
nosetattribute commands.

PROTECTIONS:

In a multi - user environment, it is necessary to provide a means to
disallow access to given disk areas/privileges/devices.  All such
permissions have only an exclude command to disallow them.  This is an
extra protection feature; once lost, there is nothing a program or user
can do to get them back.  The exclude command is used to exclude access
to a given drive, drive/user, or device:

     exclude d: - Disallows access to drive d:.

     exclude command: - Disallows access to the command directory.

     exclude d1: - Disallows access to drive d:, user 1.

     exclude lpt: - Disallows access to the lpt: device.

Excludedirect excludes the ability to perform direct disk reads and
writes, and also excludes the permission to change that mode.  Excludeio
eliminates the ability to perform direct I/O.  Excludemount takes away
the ability to mount and unmount disks.  Excludeattribute eliminates
attribute set ability.

excludetimeset disallows setting of the system time.

FLOW OF CONTROL:

A full set of flow - of - control commands are resident in rp.  Together
with the full expression ability, the exec "language" of rp is quite
capable, and in fact, quite reasonable programs can be written in it.

The prime limitation is simply speed.  Running a program off disk will
by definition not be fast.  The statements if, else, elseif, and endif
provide conditional execution.  The repeat, until, while and endwhile
commands provide looping control, and the command break will abort any
loop.

Finally, the label and goto commands allow completely arbitrary flow of
control, to any point in the program, using standard alphanumeric
labels.

There is no limit whatever on where the flow - of - control commands may
be executed.  They may be used equally well immediately from the
console, in an alias, or in a submit.  No limit exists on how far a goto
will jump, or how many repeat or while loops may be nested.

A good example of general program construction is given by bbs.sub, the
exec file that runs the bbs (temporarily, until we get a hardcoded bbs
in).

MULTITASKING/MULTIUSER:

As stated, any number of tasks may be run under rp, and any number of
users. The only limit is the available system memory used to keep track
of these tasks.  The current list of tasks running is given by the
taskstatus (tstat) (ts) command.  Several statistics will be given on
each task, including the number of task (numbered from 0, the first task
to be started in rp, to whatever task was started last), the name of the
program running, whether the task is running or stopped, if it is
attached to the console, what console it is attached to, the number of
open files, the home disk directory, the time it was started and how
long it has been running.  Tasks may be killed with the kill command, or
stopped with stop, or resumed with resume.

A task may be detached from the console with detach, or reattached with
attach.  This is done if, say, you are going to use a full screen editor
and do not want the printout from the task to disturb your work.

The command excludeglobaltask (yes, that is the longest built - in
command name) will eliminate access to tasks not connected to the
present terminal.

They will not appear in the task status, nor is any control over them
allowed.

As we have said, ANY built in command, alias, submit or application can
be run either as a "foreground" or "background" task.  Additionally, the
execution processor that receives and executes user commands is itself a
task under rp.  The execute (exec) command causes a new executive to be
branched off (an exec always runs in parallel).  The exec command is the
key to multiple user work:

     exec connect con:=crt:

If the main system is running under, say, tty:, and another terminal
(user) is available under crt:, the above statement would "start up" an
executive for that terminal.  The command:

    exec startup

Would do the same, but cause the submit file startup.sub to be executed
first, before allowing the user on that terminal to execute direct lines
from the console.  Typically, the startup file would have statements
setting the console that is to be used, the home disk area, and the
standard aliases used.  Also, exclude statementsthe bbs terminal on this
system.

If multiple tasks are allowed to run on a single terminal, the result
typically is chaos; tasks fight over the characters coming from the
keyboard, and the outputs of the two tasks mix on output.  To provide
control for this situation, you can enable a multiple task "contention"
handler for the console.  The buffer and nobuffer statements enable and
disable this mode.

In buffered mode, each task has it's own input and output line buffer,
and can output characters even when not currently being displayed on the
console.

If any task completes it's line (with cr/lf), the whole line is printed
on the console, and whatever task was "interrupted" by this is
reprinted.  In other words, lines are allowed to mix on the console, but
not characters.

All the tasks currently running under the console act as if arranged in
a circle.  If console controls are enabled (discussed in a bit), the
CTL-R character will cause the next task in the circle to be displayed
on the console.  The task being displayed is the one that gets the input
from the console, and can be controlled by console characters.

The controls and nocontrols commands enable or disable the console
control characters.  These are:

     CTL-R - Rotate to next console task.

     CTL-W - Stop presently displayed task (whether it wants to or not).

     CTL-E - Resume a task stopped by CTL-W.

     CTL-T - Terminate a task (whether it likes it or not).

     CTL-Y - Flush input queue (dispose of all waiting characters).

These characters are deliberately picked to avoid the usual controls for
a program, such as CTL-S (stop), CTL-Q (resume), CTL-C (cancel).  The
console control characters are processed without knowledge or permission
from the application running; CTL-T will terminate even a crashed
program.

A 256 byte input queue may be enabled for each console on the system.
The queue and noqueue commands enable or disable the queue.  Characters
will be saved in the input queue whether or not the program running is
accepting them.

This provides the "type ahead" function usually implemented in multitask
systems.  The CTL-Y character will cause any characters in the queue to
be disposed of.

If a task not being displayed requires input from the console, the
command demand will allow the console display to be automatically
shifted to that task.  The nodemand command disables this mode.

Experience with the multiple display system is best gained by actual
use.

THIS WAY OUT:

With such a diverse environment as that of the Z80, rp was built with
the intention of support for for multiple operating systems interfaces
and file formats.  Thus in the future, support for systems on the Z80
will be expanded.

The systems on Z80 include CP/M 2.2, CP/M 3.0, ZCPR, MP/M, Concurrent
CP/M, and OASIS being the most popular.  With the notable exception of
ZCPR, these systems all share one rather depressing characteristic;
support by the original programmers has been suspended, and that support
has not been carried on by others.  It strikes this programmer that the
attitude of both terminating support on a large piece of code AND
refusing to properly pass on the source to others smacks of a "take your
bat and ball and go home" attitude.  In designing a new O/S interface, I
think it helps to consider that you are creating a virtual standard.  A
list of calls should not be simply a random list of features, with more
features dumped at the end of the list to add function, or often to
correct deficiencies of the preceding calls.  Creating a system
interface that is regular enough to be easily ported to other systems is
the greatest favor an O/S programmer can do for applications writers,
and an acknowledgement that the applications are far more important than
the O/S itself.

Why design a custom interface for rp at all ? Starting with a clean
slate enables us to cleanly and clearly support the new features of both
rp and the Z280.  I also believe that no microprocessor interface to
date with the exception of UNIX (which did not start out on a
microprocessor) displays good quality and careful design.  Obviously,
however, each new O/S designer has come to that conclusion, accounting
for the interface to each new O/S being incompatible with others.

Rp compensates (but does not ultimately correct !) for this situation by
providing an organized solution for foreign interface support.  These
modules internal to rp are call Foreign Operating Systems, or FOS
modules.

A FOS under rp intercepts all system calls, traps and other exceptions
from a target program.  For the present version, two FOS modules are
built in to rp; the CP/M fos and the RP fos.

RP NATIVE INTERFACE:

Calls to rp are performed directly by the Z280 "sc" instruction.  Each
system call number corresponds to a different system function.  A
standard set of system error codes is returned in a (with 0 meaning no
error), and all parameters being passed in registers.  If an invalid or
unsupported system call is used, and error for that will be returned.

The system calls used are quite simple and logical.  File names are
passed as strings, and the required parsing of file names is carried out
by the O/S.

The program is not required to keep or maintain "fcb's" in it's own
memory.

Files are kept track of using small logical file numbers.

Files under rp are universal; the file string specified for opening can
be the name of a file, an I/O device, or even a logical file/device
name.

Devices can be read or written the same way as ordinary files.

Files under rp are blockless; the program can specify any number of
bytes to be read or written from 1 to 65535, without any need to perform
blocking or deblocking.

The system calls currently available under rp fos are:

     terminate - Terminates the program run.  Will also accept an
                 error code, and print the message corresponding
                 to the standard error.

     open      - Accepts a file name string, and mode flags.

                 The given file or device is opened, and
                 a logical file number from 1 to 255 is returned.

     close     - Closes a file by the logical file number.

     read      - From 1 to 65535 bytes are read from the given file
                 to program memory.

     write     - From 1 to 65535 bytes are written to the file
                 from program memory.

     location  - The current byte location in the file is returned.

     position  - The file position is moved to the given location.

     length    - The number of bytes in a file are returned.

     size      - The number of bytes in a file are set (the file
                 is truncated).

     copy      - A file by a given string is copied to the file
                 given in a destination string.

     move      - A file by a given string is moved to the file
                 given in a destination string.

     time      - Returns the current time and date.

     schedule  - Accepts a time and date, and stops the program
                 until PAST that time and date (if the date given
                 has already past, return is immediate).
